
/**
 * @license
 * Copyright 2022 Du Tian Wei
 * SPDX-License-Identifier: Apache-2.0
 */
import * as obvm from '../../runtime/vm.mjs'

class IframeParentConnector {
    // joined = false;
    // listener;
    // ob_network;
    constructor(obn) {
        this.joined = false;
        this.ob_network = obn;
        this.listener = this.onMessage.bind(this);
    }
    is_joined() {
        return this.joined;
    }
    join() {
        if (this.joined) {
            return;
        }
        window.addEventListener('message', this.listener, true);
        window.parent.postMessage({ module: 'ob-network', cmd: 'join' });
        this.joined = true;
    }
    leave() {
        window.parent.postMessage({ module: 'ob-network', cmd: 'leave' });
        window.removeEventListener('message', this.listener);
        this.joined = false;
    }
    onMessage(evt) {
        if (evt.data.module == 'ob-network') {
            switch (evt.data.cmd) {
                case 'peer-join':
                    this.ob_network.onPeerJoin(evt.data.name);
                    break;
                case 'peer-leave':
                    this.ob_network.onPeerLeave(evt.data.name);
                    break;
                case 'message':
                    this.ob_network.onMessage(evt.data.src, evt.data.title, evt.data.data, evt.data.regType, evt.data.dataType);
                    break;
            }
        }
    }
    sendMessage(target, title, data, regType, dataType) {
        window.parent.postMessage({ module: 'ob-network', cmd: 'message', target, title, data, regType, dataType });
    }
}
class RemoteServerConnector { }
class OBNetworkImpl {
    // fsms = [];
    // joined_peers = {};
    // connector;
    // obvm;
    constructor() {
        this.fsms = [];
        this.joined_peers = {};
        this.destoryCallback = this.on_fsm_destoryed.bind(this);
    }
    onPeerJoin(name) {
        this.joined_peers[name] = true;
        this.obvm.BroadcastMessage(new obvm.OBEventMessage('network_peer_join', 'Network_target', name, null));
    }
    onPeerLeave(name) {
        delete this.joined_peers[name];
        this.obvm.BroadcastMessage(new obvm.OBEventMessage('network_peer_leave', 'Network_target', name, null));
    }
    join(obvm) {
        if (this.connector && this.connector.is_joined()) {
            return;
        }
        this.obvm = obvm;
        this.connector = window.parent == window ? new RemoteServerConnector(this) : new IframeParentConnector(this);
        this.connector.join();
        console.log('join network');
    }
    is_joined() {
        return !!(this.connector && this.connector.is_joined());
    }
    leave() {
        if (this.connector && this.connector.is_joined()) {
            console.log('leave network');
            this.connector.leave();
        }
    }
    set_enable(fsm, enable) {
        if (enable) {
            this.enableFSM(fsm);
        } else {
            this.disableFSM(fsm);
        }
    }
    disableFSM(fsm) {
        let enabled = this.is_enabled(fsm);
        if (!enabled) {
            console.log('fsm not enabled');
            return;
        }
        let idx = this.fsms.indexOf(fsm);
        this.fsms.splice(idx, 1);
        fsm.OffDestroyed(this.destoryCallback);
        console.log('fsm network port off');
    }
    enableFSM(fsm) {
        let enabled = this.is_enabled(fsm);
        if (enabled) {
            console.log('already enabled');
            return;
        }
        this.fsms.push(fsm);
        fsm.OnDestroyed(this.destoryCallback);
        console.log('fsm network port on');
    }
    on_fsm_destoryed(fsm) {
        this.set_enable(fsm, false);
    }
    is_enabled(fsm) {
        let enabled = this.fsms.indexOf(fsm) > -1;
        return enabled;
    }
    sendMessage(target, title, data, regType, dataType) {
        if (this.connector && this.connector.is_joined()) {
            console.log('send network message');
            this.connector.sendMessage(target, title, data, regType, dataType);
        }
    }
    onMessage(src, title, data, regType, dataType) {
        this.fsms.forEach(fsm => {
            fsm.PostMessage(new obvm.OBUserMessage(title, dataType, data, null));
        });
    }
}
let impl = new OBNetworkImpl();
function join_installer(builder, args) {
    builder.PushAction((st, f, local, pos) => {
        impl.join(st.fsm.VM);
        return pos + 1;
    });
}
function leave_installer(builder, args) {
    builder.PushAction((st, f, local, pos) => {
        impl.leave();
        return pos + 1;
    });
}
function set_enable_installer(builder, args) {
    let argIdx = args[1] & 0xFFF;
    let arg = builder.LongRegister[argIdx];
    builder.PushAction((st, f, local, pos) => {
        let argVal = arg(st, f, local);
        impl.set_enable(st.fsm, argVal);
        return pos + 1;
    });
}
function is_enabled_installer(builder, args) {
    let retRegIdx = args[0];
    retRegIdx = retRegIdx & 0xFFF;
    builder.LongRegister[retRegIdx] = (st, f, local) => {
        return impl.is_enabled(st.fsm) ? 1 : 0;
    };
}
function is_joined_installer(builder, args) {
    let retRegIdx = args[0];
    retRegIdx = retRegIdx & 0xFFF;
    builder.LongRegister[retRegIdx] = (st, f, local) => {
        return impl.is_joined() ? 1 : 0;
    };
}
function send_message_install(builder, args) {
    let targetIdx = args[1];
    targetIdx = targetIdx & 0xFFF;
    let targetF = builder.NObjectRegister[targetIdx];

    let titleIdx = args[2];
    titleIdx = titleIdx & 0xFFF;
    let titleF = builder.StringRegister[titleIdx];

    let dataIdx = args[3];
    let dataRegType = (dataIdx & 0xF000) >> 12;
    dataIdx = dataIdx & 0xFFF;

    let dataF;
    switch (dataRegType) {
        case 0:
            dataF = builder.LongRegister[dataIdx];
            break;
        case 1:
            dataF = builder.DoubleRegister[dataIdx];
            break;
        case 2:
            dataF = builder.StringRegister[dataIdx];
            break;
        case 3:
            dataF = builder.StructRegister[dataIdx];
            break;
        case 4:
            dataF = builder.NObjectRegister[dataIdx];
            break;
    }
    builder.PushAction((st, f, local, pos) => {
        let target = targetF(st, f, local);
        let title = titleF(st, f, local);
        let data = dataF(st, f, local);
        let dataType = obvm.OBMessage.ArgTypeOf(dataRegType, data);
        impl.sendMessage(target, title, data, dataRegType, dataType);
        return pos + 1;
    });
}
class OBNetwork {
    /**
    * 安装到脚本库
    * @param {OBScript} script 
    */
    static install(script) {
        script.InstallLib("network", "network", [
            join_installer,
            leave_installer,
            is_joined_installer,
            set_enable_installer,
            is_enabled_installer,
            send_message_install
        ]);
    }
}
export { OBNetwork };